package aupdg.presentation.adapter.impl;

import aupdg.data.DirectedGraphCustom;
import aupdg.presentation.adapter.LdgToJPanel;
import aupdg.logic.analysis.SecurityLevel;
import aupdg.data.model.Edge;
import aupdg.data.model.Ldg;
import aupdg.data.model.Node;
import aupdg.data.model.impl.AuDataDependenceEdge;
import aupdg.data.model.impl.AuLdgData;
import edu.uci.ics.jung.algorithms.layout.FRLayout;
import edu.uci.ics.jung.algorithms.layout.LDGLayout;
import edu.uci.ics.jung.algorithms.layout.Layout;
import edu.uci.ics.jung.visualization.GraphZoomScrollPane;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse;
import edu.uci.ics.jung.visualization.control.ModalGraphMouse;
import edu.uci.ics.jung.visualization.decorators.ToStringLabeller;
import java.awt.*;
import java.util.List;
import javax.swing.JPanel;
import org.apache.commons.collections15.Transformer;

/**
 * Implements the contract LdgToJPanel for generating a JPanel which contains a
 * graphic representation of an LDG.
 *
 * @author AA and CJ
 */
public class LdgToJPanelImpl implements LdgToJPanel {

    private GraphZoomScrollPane zoomPanel;

    /**
     * Constructor. It constructs a GraphZoomScrollPane from the LDG.
     *
     * @param ldg
     * @param gm
     */
    public LdgToJPanelImpl(Ldg ldg, DefaultModalGraphMouse gm) {


        final DirectedGraphCustom graph = new LdgToDirectedGraphImpl().convert(ldg);

        System.out.println("GRAPH TO BE RENDERED");
        System.out.println(graph);

        //Layout<Node, Edge> layout = new FRLayout<>(graph);
        Layout<Node, Edge> layout = new LDGLayout<>(graph);
        layout.setSize(new Dimension(500, 400));
        VisualizationViewer<Node, Edge> vv = new VisualizationViewer<>(
                layout);

        zoomPanel = new GraphZoomScrollPane(vv);

        //vv.setPreferredSize(new Dimension(300, 300));
        vv.setBackground(Color.white);
        //vv.setOpaque(false);

        // Show vertex and edge labels
        vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller());
        vv.getRenderContext().setEdgeLabelTransformer(new ToStringLabeller());

        //set stroke of edges
        float dash[] = {10.0f};
        final Stroke edgeStroke = new BasicStroke(1.0f, BasicStroke.CAP_BUTT,
                BasicStroke.JOIN_MITER, 10.0f, dash, 0.0f);
        final Stroke edgeDefStroke = new BasicStroke();
        Transformer<Edge, Stroke> edgeStrokeTransformer = new Transformer<Edge, Stroke>() {

            @Override
            public Stroke transform(Edge e) {
                if (graph.isDataEdge(e)) {
                    //TODO CJC change here, check for data dependence edges and 
                    //choose dash lines
                    return edgeStroke;
                } else {
                    return edgeDefStroke;
                }
            }
        };

        Transformer<Edge, Paint> edgeColorTransformer = new Transformer<Edge, Paint>() {

            @Override
            public Paint transform(Edge edge) {
                if (edge instanceof AuDataDependenceEdge) {
                    AuDataDependenceEdge datDepEdge = (AuDataDependenceEdge) edge;
                    List<AuLdgData> dataObjects = datDepEdge.getDataObjects();
                    boolean allDataObjectsAreHigh = true;
                    boolean someObjectsAreHigh = false;
                    for (int i = 0; i < dataObjects.size(); i++) {
                        if (dataObjects.get(i).getSecurityLevel() == SecurityLevel.LOW) {
                            allDataObjectsAreHigh = false;
                        } else {
                            someObjectsAreHigh = true;
                        }
                    }
                    if (allDataObjectsAreHigh) {
                        return Color.RED;
                    } else if (someObjectsAreHigh) {
                        return Color.BLUE;
                    }
                }
                return Color.BLACK;
            }
        };

        Transformer<Edge, String> edgeLabelTransformer = new Transformer<Edge, String>() {

            @Override
            public String transform(Edge edge) {
                if (edge instanceof AuDataDependenceEdge) {
                    AuDataDependenceEdge datDepEdge = (AuDataDependenceEdge) edge;
                    List<AuLdgData> dataObjects = datDepEdge.getDataObjects();
                    boolean allDataObjectsAreHigh = true;
                    boolean someObjectsAreHigh = false;
                    for (int i = 0; i < dataObjects.size(); i++) {
                        if (dataObjects.get(i).getSecurityLevel() == SecurityLevel.LOW) {
                            allDataObjectsAreHigh = false;
                        } else {
                            someObjectsAreHigh = true;
                        }
                    }
                    if (allDataObjectsAreHigh) {
                        return "<html><font color=\"red\">" + edge.toString();
                    } else if (someObjectsAreHigh) {
                        return "<html><font color=\"blue\">" + edge.toString();
                    }
                }
                return "<html><font color=\"black\">" + edge.toString();
            }
        };

        Transformer<Node, Paint> vertexColor = new Transformer<Node, Paint>() {

            @Override
            public Paint transform(Node node) {
                if (node.getLevel() == SecurityLevel.HIGH) {
                    return Color.RED;
                }
                return Color.GREEN;
            }
        };

        vv.getRenderContext().setEdgeStrokeTransformer(edgeStrokeTransformer);
        vv.getRenderContext().setEdgeDrawPaintTransformer(edgeColorTransformer);
        vv.getRenderContext().setVertexFillPaintTransformer(vertexColor);
        vv.getRenderContext().setEdgeLabelTransformer(edgeLabelTransformer);
        // Create a graph mouse and add it to the visualization component
        //DefaultModalGraphMouse gm = new DefaultModalGraphMouse();
        //gm.setMode(ModalGraphMouse.Mode.TRANSFORMING);
        vv.setGraphMouse(gm);

        //JPanel panel = new JPanel();

        // menu for changing mouse modes
        //modeMenu = gm.getModeMenu(); // Obtain mode menu from the mouse
        //modeMenu.setText("Mouse Mode");
        //modeMenu.setIcon(null); 
        //modeMenu.setPreferredSize(new Dimension(80,20)); 
        vv.revalidate();
        vv.repaint();

        gm.setMode(ModalGraphMouse.Mode.EDITING);

        //menuBar.add(modeMenu);
        //this.add(vv, BorderLayout.CENTER);
        //this.setOpaque(false);
        //this.revalidate();
        //this.repaint();
    }

    @Override
    public JPanel renderLdg() {
        zoomPanel.setOpaque(false);
        zoomPanel.revalidate();
        zoomPanel.repaint();
        zoomPanel.setVisible(true);
        return zoomPanel;
    }
}
